﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using Tsanie.Properties;
using Tsanie.ShellExtension;
using Microsoft.Win32;
using System.Threading;
using System.Drawing.Imaging;

namespace Tsanie {
    public partial class ConfigForm : Form {

        #region - Fields -

        private const int WM_KEYDOWN = 0x100;
        private const int WM_KEYUP = 0x101;
        private const int WM_CHAR = 0x102;
        private const int ITEMHEIGHT = 22;

        private static ConfigForm _form = null;
        public static ConfigForm Form {
            get {
                if (_form == null)
                    _form = new ConfigForm();
                return _form;
            }
        }

        private Dictionary<string, Bitmap[]> _cacheFontBitmaps;
        private Brush _brush;
        private StringFormat _fmt;
        private string fontFamily;
        private float fontSize;

        private Panel panelThumbnail;
        private Panel panelBackground;
        private NumericUpDown numericFontsize;
        private ComboBox comboFontFamily;
        private PictureBox picturePreview;
        private Button buttonApply;
        private Button buttonCancel;
        private Button buttonOK;

        #endregion

        #region - Overrides -

        public ConfigForm() {
            _cacheFontBitmaps = new Dictionary<string, Bitmap[]>();
            _brush = new SolidBrush(Color.Black);
            _fmt = new StringFormat() { LineAlignment = StringAlignment.Center, FormatFlags = StringFormatFlags.NoWrap, Trimming = StringTrimming.EllipsisCharacter };
            InitComponent();
        }
        protected override void OnLoad(EventArgs e) {
            base.OnLoad(e);
            Font fnt = TextThumbnail.GetSavedFont();
            fontFamily = fnt.Name;
            fontSize = fnt.Size;
            numericFontsize.Value = (decimal)fontSize;
        }
        protected override void OnShown(EventArgs e) {
            base.OnShown(e);
            new Thread(delegate() {
                int n = 0;
                foreach (FontFamily font in new System.Drawing.Text.InstalledFontCollection().Families) {
                    try {
                        FontItem fnt = new FontItem() { Font = new Font(font, 11f) };
                        this.Invoke(new MethodInvoker(delegate() {
                            comboFontFamily.Items.Add(fnt);
                            if (font.Name == fontFamily)
                                comboFontFamily.SelectedIndex = n;
                        }));
                        n++;
                    } catch { }
                }
                try {
                    this.Invoke(new MethodInvoker(delegate() {
                        if (comboFontFamily.SelectedIndex < 0)
                            comboFontFamily.SelectedIndex = 0;
                        RefreshPreview();
                        this.comboFontFamily.SelectedIndexChanged += new EventHandler(comboFontFamily_SelectedIndexChanged);
                        this.comboFontFamily.Enabled = true;
                        this.numericFontsize.ValueChanged += new EventHandler(numericFontsize_ValueChanged);
                        this.numericFontsize.Enabled = true;
                        this.buttonOK.Click += new EventHandler(buttonOK_Click);
                        this.buttonOK.Enabled = true;
                    }));
                } catch (Exception ex) {
                    Common.OutputDebugString("OnShown(EventArgs):: Loaded fonts, {0} - {1}", ex.GetType().FullName, ex.Message);
                }
            }) { Name = "thread_LoadFonts" }.Start();
        }
        protected override void OnFormClosed(FormClosedEventArgs e) {
            base.OnFormClosed(e);
            ConfigForm._form = null;
        }
        protected override bool ProcessKeyPreview(ref Message m) {
            if (m.Msg == WM_KEYDOWN) {
                if (m.WParam.ToInt32() == 0x1b) {
                    this.buttonCancel.PerformClick();
                    return true;
                }
            }
            return base.ProcessKeyPreview(ref m);
        }

        #endregion

        private void RefreshPreview() {
            picturePreview.Image = TextThumbnail.GetThumbnail(new Font(fontFamily, fontSize), Resource.PreviewText, null);
        }

        #region - Events -

        private void comboFontFamily_MeasureItem(object sender, MeasureItemEventArgs e) {
            e.ItemHeight = ITEMHEIGHT;
        }
        private void comboFontFamily_DrawItem(object sender, DrawItemEventArgs e) {
            e.DrawBackground();
            if (e.Index < 0)
                return;
            FontItem font = (FontItem)comboFontFamily.Items[e.Index];
            string name = font.ToString();
            // 是否绘制当前项
            if ((e.State & DrawItemState.ComboBoxEdit) == DrawItemState.ComboBoxEdit) {
                e.Graphics.DrawString(name, e.Font, new SolidBrush(e.ForeColor), e.Bounds, _fmt);
                return;
            }

            // 开始列表项
            Bitmap[] bmp;
            if (!_cacheFontBitmaps.TryGetValue(name, out bmp)) {
                Rectangle rect = new Rectangle(0, 0, e.Bounds.Width, e.Bounds.Height);
                bmp = new Bitmap[2];
                bmp[0] = new Bitmap(rect.Width, rect.Height, PixelFormat.Format32bppArgb);
                Graphics g = Graphics.FromImage(bmp[0]);
                //g.TextContrast = 0;
                g.TextRenderingHint = System.Drawing.Text.TextRenderingHint.AntiAlias;
                g.DrawString(name, font.Font, _brush, rect, _fmt);
                g.Dispose();
                bmp[1] = new Bitmap(bmp[0]);
                BitmapData bdata = bmp[1].LockBits(rect, ImageLockMode.ReadWrite, PixelFormat.Format32bppArgb);
                int offset = bdata.Stride - rect.Width * 4;
                unsafe {
                    byte* pt = (byte*)bdata.Scan0.ToPointer();
                    for (int y = 0; y < rect.Height; y++) {
                        for (int x = 0; x < rect.Width; x++) {
                            if (pt[3] > 0) {    // Alpha
                                pt[0] = (byte)(255 - pt[0]);
                                pt[1] = (byte)(255 - pt[1]);
                                pt[2] = (byte)(255 - pt[2]);
                            }
                            pt += 4;
                        }
                        pt += offset;
                    }
                }
                bmp[1].UnlockBits(bdata);

                _cacheFontBitmaps.Add(name, bmp);
            }
#if DEBUG
            Common.OutputDebugString("DrawItem():: {0}, rect = {1}, state = {2}", name, e.Bounds, e.State);
#endif
            if ((e.State & DrawItemState.Selected) == DrawItemState.Selected) {
                e.Graphics.DrawImage(bmp[1], e.Bounds);
            } else {
                e.Graphics.DrawImage(bmp[0], e.Bounds);
            }
        }
        private void numericFontsize_ValueChanged(object sender, EventArgs e) {
            fontSize = (float)numericFontsize.Value;
            RefreshPreview();
            buttonApply.Enabled = true;
        }
        private void comboFontFamily_SelectedIndexChanged(object sender, EventArgs e) {
            int index = comboFontFamily.SelectedIndex;
            if (index < 0)
                return;
            fontFamily = comboFontFamily.Items[index].ToString();
            RefreshPreview();
            buttonApply.Enabled = true;
        }
        private void buttonApply_Click(object sender, EventArgs e) {
            Font fnt = new Font(fontFamily, fontSize);
            TextThumbnail.Font = fnt;
            RegistryKey root = Registry.CurrentUser.OpenSubKey(@"Software\Tsanie\TextThumbnail", true);
            if (root == null) {
                // 首次，注册键值
                root = Registry.CurrentUser.CreateSubKey(@"Software\Tsanie\TextThumbnail");
            }
            root.SetValue("FontFamily", fnt.Name, RegistryValueKind.String);
            root.SetValue("FontSize", (int)(fnt.Size * 10), RegistryValueKind.DWord);
            root.Close();
            buttonApply.Enabled = false;
        }
        private void buttonCancel_Click(object sender, EventArgs e) {
            this.Close();
        }
        private void buttonOK_Click(object sender, EventArgs e) {
            if (buttonApply.Enabled)
                buttonApply.PerformClick();
            this.Close();
        }
        private void treeParam_AfterSelect(object sender, TreeViewEventArgs e) {
            switch (e.Node.Name) {
                case "NodeThumbnail":
                    panelThumbnail.Visible = true;
                    panelBackground.Visible = false;
                    break;
                case "NodeBackground":
                    panelBackground.Visible = true;
                    panelThumbnail.Visible = false;
                    break;
            }
        }

        #endregion

        #region - Designer -

        /// <summary>
        /// Required designer variable.
        /// </summary>
        private System.ComponentModel.IContainer components = null;

        /// <summary>
        /// Clean up any resources being used.
        /// </summary>
        /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
        protected override void Dispose(bool disposing) {
            if (disposing && (components != null)) {
                components.Dispose();
            }
            base.Dispose(disposing);
        }

        private void InitComponent() {
            this.components = new Container();
            this.panelThumbnail = new Panel();
            this.comboFontFamily = new ComboBox();
            this.numericFontsize = new NumericUpDown();
            this.panelBackground = new Panel();
            this.picturePreview = new PictureBox();
            this.buttonApply = new Button();
            this.buttonCancel = new Button();
            this.buttonOK = new Button();
            TreeView treeParam = new TreeView();
            TreeNode nodeThumbnail = new TreeNode(Resource.NodeThumbnail);
            TreeNode nodeGeneral = new TreeNode(Resource.NodeGeneral, new TreeNode[] { nodeThumbnail });
            TreeNode nodeBackground = new TreeNode(Resource.NodeBackground);
            ImageList imageParam = new ImageList(this.components);
            GroupBox groupThumbnailPreview = new GroupBox();
            GroupBox groupThumbnailFont = new GroupBox();
            Label labelFontFamily = new Label();
            Label labelFontsize = new Label();
            Label labelThumbnail = new Label();
            Label labelBackground = new Label();

            this.panelThumbnail.SuspendLayout();
            groupThumbnailPreview.SuspendLayout();
            ((ISupportInitialize)(this.picturePreview)).BeginInit();
            groupThumbnailFont.SuspendLayout();
            ((ISupportInitialize)(this.numericFontsize)).BeginInit();
            this.panelBackground.SuspendLayout();
            this.SuspendLayout();
            // 
            // imageParam
            // 
            imageParam.ImageStream = Resource.ImageStream;
            imageParam.TransparentColor = System.Drawing.Color.Transparent;
            imageParam.Images.SetKeyName(0, "ConfigThumbnail");
            imageParam.Images.SetKeyName(1, "ConfigImage");
            imageParam.Images.SetKeyName(2, "ConfigGeneral");
            // 
            // treeParam
            // 
            treeParam.BorderStyle = BorderStyle.FixedSingle;
            treeParam.HotTracking = true;
            treeParam.ImageList = imageParam;
            treeParam.ItemHeight = 18;
            treeParam.Location = new Point(6, 6);
            nodeThumbnail.ImageKey = "ConfigThumbnail";
            nodeThumbnail.Name = "NodeThumbnail";
            nodeThumbnail.SelectedImageKey = "ConfigThumbnail";
            nodeGeneral.ImageKey = "ConfigGeneral";
            nodeGeneral.Name = "NodeGeneral";
            nodeGeneral.SelectedImageKey = "ConfigGeneral";
            nodeGeneral.Expand();
            nodeBackground.ImageKey = "ConfigImage";
            nodeBackground.Name = "NodeBackground";
            nodeBackground.SelectedImageKey = "ConfigImage";
            treeParam.Nodes.AddRange(new TreeNode[] { nodeGeneral, nodeBackground });
            treeParam.Size = new Size(175, 281);
            treeParam.AfterSelect += new TreeViewEventHandler(this.treeParam_AfterSelect);
            // 
            // panelThumbnail
            // 
            this.panelThumbnail.BackColor = Color.White;
            this.panelThumbnail.BorderStyle = BorderStyle.FixedSingle;
            this.panelThumbnail.Controls.Add(groupThumbnailPreview);
            this.panelThumbnail.Controls.Add(groupThumbnailFont);
            this.panelThumbnail.Controls.Add(labelThumbnail);
            this.panelThumbnail.Location = new Point(187, 6);
            this.panelThumbnail.Size = new Size(329, 281);
            // 
            // groupThumbnailPreview
            // 
            groupThumbnailPreview.Controls.Add(this.picturePreview);
            groupThumbnailPreview.Location = new Point(8, 116);
            groupThumbnailPreview.Size = new Size(311, 156);
            groupThumbnailPreview.TabStop = false;
            groupThumbnailPreview.Text = Resource.GroupPreview;
            // 
            // picturePreview
            // 
            this.picturePreview.Location = new Point(8, 19);
            this.picturePreview.Size = new Size(110, 130);
            this.picturePreview.Image = Resource.textBackground;
            this.picturePreview.SizeMode = PictureBoxSizeMode.StretchImage;
            this.picturePreview.TabStop = false;
            // 
            // groupThumbnailFont
            // 
            groupThumbnailFont.Controls.Add(this.comboFontFamily);
            groupThumbnailFont.Controls.Add(labelFontFamily);
            groupThumbnailFont.Controls.Add(this.numericFontsize);
            groupThumbnailFont.Controls.Add(labelFontsize);
            groupThumbnailFont.Location = new Point(8, 29);
            groupThumbnailFont.Size = new Size(311, 81);
            groupThumbnailFont.TabStop = false;
            groupThumbnailFont.Text = Resource.GroupFont;
            // 
            // comboFontFamily
            // 
            this.comboFontFamily.FormattingEnabled = true;
            this.comboFontFamily.Location = new Point(90, 44);
            this.comboFontFamily.Size = new Size(215, 20);
            this.comboFontFamily.Enabled = false;
            this.comboFontFamily.DropDownStyle = ComboBoxStyle.DropDownList;
            this.comboFontFamily.DropDownHeight = 400;
            this.comboFontFamily.DrawMode = DrawMode.OwnerDrawVariable;
            this.comboFontFamily.MeasureItem += new MeasureItemEventHandler(comboFontFamily_MeasureItem);
            this.comboFontFamily.DrawItem += new DrawItemEventHandler(comboFontFamily_DrawItem);
            // 
            // labelFontFamily
            // 
            labelFontFamily.AutoSize = true;
            labelFontFamily.Location = new Point(6, 47);
            labelFontFamily.Text = Resource.LabelFontFamily;
            // 
            // numericFontsize
            // 
            this.numericFontsize.Location = new Point(90, 17);
            this.numericFontsize.DecimalPlaces = 1;
            this.numericFontsize.Size = new Size(93, 21);
            this.numericFontsize.Enabled = false;
            // 
            // labelFontsize
            // 
            labelFontsize.AutoSize = true;
            labelFontsize.Location = new Point(6, 21);
            labelFontsize.Text = Resource.LabelFontSize;
            // 
            // labelThumbnail
            // 
            labelThumbnail.BackColor = SystemColors.ControlDark;
            labelThumbnail.Font = new Font(this.Font, FontStyle.Bold);
            labelThumbnail.ForeColor = Color.White;
            labelThumbnail.Location = new Point(2, 2);
            labelThumbnail.Padding = new Padding(4, 0, 0, 0);
            labelThumbnail.Size = new Size(323, 20);
            labelThumbnail.Text = Resource.NodeThumbnail;
            labelThumbnail.TextAlign = ContentAlignment.MiddleLeft;
            // 
            // panelBackground
            // 
            this.panelBackground.BackColor = Color.White;
            this.panelBackground.BorderStyle = BorderStyle.FixedSingle;
            this.panelBackground.Controls.Add(labelBackground);
            this.panelBackground.Location = new Point(187, 6);
            this.panelBackground.Size = new Size(329, 281);
            this.panelBackground.Visible = false;
            // 
            // labelBackground
            // 
            labelBackground.BackColor = SystemColors.ControlDark;
            labelBackground.Font = new Font(this.Font, FontStyle.Bold);
            labelBackground.ForeColor = Color.White;
            labelBackground.Location = new Point(2, 2);
            labelBackground.Padding = new Padding(4, 0, 0, 0);
            labelBackground.Size = new Size(323, 20);
            labelBackground.Text = Resource.NodeBackground;
            labelBackground.TextAlign = ContentAlignment.MiddleLeft;
            // 
            // buttonOK
            // 
            this.buttonOK.Enabled = false;
            this.buttonOK.Location = new Point(279, 293);
            this.buttonOK.Size = new Size(75, 23);
            this.buttonOK.Text = "OK";
            this.buttonOK.UseVisualStyleBackColor = true;
            // 
            // buttonCancel
            // 
            this.buttonCancel.DialogResult = DialogResult.Cancel;
            this.buttonCancel.Location = new Point(360, 293);
            this.buttonCancel.Size = new Size(75, 23);
            this.buttonCancel.Text = "Cancel";
            this.buttonCancel.UseVisualStyleBackColor = true;
            this.buttonCancel.Click += new EventHandler(buttonCancel_Click);
            // 
            // buttonApply
            // 
            this.buttonApply.Enabled = false;
            this.buttonApply.Location = new Point(441, 293);
            this.buttonApply.Size = new Size(75, 23);
            this.buttonApply.Text = "Apply";
            this.buttonApply.UseVisualStyleBackColor = true;
            this.buttonApply.Click += new EventHandler(buttonApply_Click);
            // 
            // ConfigForm
            // 
            //this.AutoScaleDimensions = new SizeF(6F, 12F);
            //this.AutoScaleMode = AutoScaleMode.Font;
            //this.CancelButton = this.buttonCancel;
            this.ClientSize = new Size(523, 321);
            this.Controls.Add(treeParam);
            this.Controls.Add(this.panelThumbnail);
            this.Controls.Add(this.panelBackground);
            this.Controls.Add(this.buttonOK);
            this.Controls.Add(this.buttonCancel);
            this.Controls.Add(this.buttonApply);
            this.FormBorderStyle = FormBorderStyle.FixedSingle;
            this.MaximizeBox = false;
            this.MinimizeBox = false;
            this.ShowInTaskbar = false;
            this.StartPosition = FormStartPosition.CenterScreen;
            this.Icon = Resource.FormIcon;
            this.Text = Resource.FormTitle;

            this.panelThumbnail.ResumeLayout(false);
            groupThumbnailPreview.ResumeLayout(false);
            ((ISupportInitialize)(this.picturePreview)).EndInit();
            groupThumbnailFont.ResumeLayout(false);
            groupThumbnailFont.PerformLayout();
            ((ISupportInitialize)(this.numericFontsize)).EndInit();
            this.panelBackground.ResumeLayout(false);
            this.ResumeLayout(false);

        }

        #endregion

    }

    class FontItem {
        public Font Font;
        public override string ToString() {
            return Font == null ? null : Font.Name;
        }
    }
}
